/*
* Tencent is pleased to support the open source community by making Puerts available.
* Copyright (C) 2020 THL A29 Limited, a Tencent company.  All rights reserved.
* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms.
* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package.
*/

#include "DelegateWrapper.h"
#include "ObjectMapper.h"

namespace puerts
{
    v8::Local<v8::FunctionTemplate> FDelegateWrapper::ToFunctionTemplate(v8::Isolate* Isolate)
    {
        v8::EscapableHandleScope HandleScope(Isolate);
        auto Result = v8::FunctionTemplate::New(Isolate, New);
        Result->InstanceTemplate()->SetInternalFieldCount(2);

        Result->PrototypeTemplate()->Set(FV8Utils::InternalString(Isolate, "Bind"), v8::FunctionTemplate::New(Isolate, Bind));
        Result->PrototypeTemplate()->Set(FV8Utils::InternalString(Isolate, "IsBound"), v8::FunctionTemplate::New(Isolate, IsBound));
        Result->PrototypeTemplate()->Set(FV8Utils::InternalString(Isolate, "Unbind"), v8::FunctionTemplate::New(Isolate, Unbind));
        Result->PrototypeTemplate()->Set(FV8Utils::InternalString(Isolate, "Execute"), v8::FunctionTemplate::New(Isolate, Execute));

        return HandleScope.Escape(Result);
    }

    void FDelegateWrapper::New(const v8::FunctionCallbackInfo<v8::Value>& Info)
    {
    }

    void FDelegateWrapper::IsBound(const v8::FunctionCallbackInfo<v8::Value>& Info)
    {
        v8::Isolate* Isolate = Info.GetIsolate();
        v8::Isolate::Scope IsolateScope(Isolate);
        v8::HandleScope HandleScope(Isolate);
        v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
        v8::Context::Scope ContextScope(Context);

        auto DelegatePtr = FV8Utils::GetPoninterFast<FScriptDelegate>(Info.Holder(), 0);

        Info.GetReturnValue().Set(DelegatePtr->IsBound());
    }

    void FDelegateWrapper::Bind(const v8::FunctionCallbackInfo<v8::Value>& Info)
    {
        v8::Isolate* Isolate = Info.GetIsolate();
        v8::Isolate::Scope IsolateScope(Isolate);
        v8::HandleScope HandleScope(Isolate);
        v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
        v8::Context::Scope ContextScope(Context);

        if (Info.Length() == 1 && Info[0]->IsFunction())
        {
            auto DelegatePtr = FV8Utils::GetPoninterFast<void>(Info.Holder(), 0);
            FV8Utils::IsolateData<IObjectMapper>(Isolate)->AddToDelegate(Isolate, Context, DelegatePtr, v8::Local<v8::Function>::Cast(Info[0]));
        }
        else
        {
            FV8Utils::ThrowException(Isolate, "invalid arguments");
        }
    }

    void FDelegateWrapper::Unbind(const v8::FunctionCallbackInfo<v8::Value>& Info)
    {
        v8::Isolate* Isolate = Info.GetIsolate();
        v8::Isolate::Scope IsolateScope(Isolate);
        v8::HandleScope HandleScope(Isolate);
        v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
        v8::Context::Scope ContextScope(Context);

        auto DelegatePtr = FV8Utils::GetPoninterFast<void>(Info.Holder(), 0);
        FV8Utils::IsolateData<IObjectMapper>(Isolate)->ClearDelegate(Isolate, Context, DelegatePtr);
    }

    void FDelegateWrapper::Execute(const v8::FunctionCallbackInfo<v8::Value>& Info)
    {
        v8::Isolate* Isolate = Info.GetIsolate();
        v8::Isolate::Scope IsolateScope(Isolate);
        v8::HandleScope HandleScope(Isolate);
        v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
        v8::Context::Scope ContextScope(Context);

        auto DelegatePtr = FV8Utils::GetPoninterFast<void>(Info.Holder(), 0);
        FV8Utils::IsolateData<IObjectMapper>(Isolate)->ExecuteDelegate(Isolate, Context, Info, DelegatePtr);
    }

    v8::Local<v8::FunctionTemplate> FMulticastDelegateWrapper::ToFunctionTemplate(v8::Isolate* Isolate)
    {
        v8::EscapableHandleScope HandleScope(Isolate);
        auto Result = v8::FunctionTemplate::New(Isolate, New);
        Result->InstanceTemplate()->SetInternalFieldCount(2);

        Result->PrototypeTemplate()->Set(FV8Utils::InternalString(Isolate, "Add"), v8::FunctionTemplate::New(Isolate, Add));
        Result->PrototypeTemplate()->Set(FV8Utils::InternalString(Isolate, "Remove"), v8::FunctionTemplate::New(Isolate, Remove));
        Result->PrototypeTemplate()->Set(FV8Utils::InternalString(Isolate, "Clear"), v8::FunctionTemplate::New(Isolate, Clear));
        Result->PrototypeTemplate()->Set(FV8Utils::InternalString(Isolate, "Broadcast"), v8::FunctionTemplate::New(Isolate, Broadcast));

        return HandleScope.Escape(Result);
    }

    void FMulticastDelegateWrapper::New(const v8::FunctionCallbackInfo<v8::Value>& Info)
    {
    }

    void FMulticastDelegateWrapper::Add(const v8::FunctionCallbackInfo<v8::Value>& Info)
    {
        v8::Isolate* Isolate = Info.GetIsolate();
        v8::Isolate::Scope IsolateScope(Isolate);
        v8::HandleScope HandleScope(Isolate);
        v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
        v8::Context::Scope ContextScope(Context);

        if (Info.Length() == 1 && Info[0]->IsFunction())
        {
            auto DelegatePtr = FV8Utils::GetPoninterFast<void>(Info.Holder(), 0);
            FV8Utils::IsolateData<IObjectMapper>(Isolate)->AddToDelegate(Isolate, Context, DelegatePtr, v8::Local<v8::Function>::Cast(Info[0]));
        }
        else
        {
            FV8Utils::ThrowException(Isolate, "invalid arguments");
        }
    }

    void FMulticastDelegateWrapper::Remove(const v8::FunctionCallbackInfo<v8::Value>& Info)
    {
        v8::Isolate* Isolate = Info.GetIsolate();
        v8::Isolate::Scope IsolateScope(Isolate);
        v8::HandleScope HandleScope(Isolate);
        v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
        v8::Context::Scope ContextScope(Context);

        if (Info.Length() == 1 && Info[0]->IsFunction())
        {
            auto DelegatePtr = FV8Utils::GetPoninterFast<void>(Info.Holder(), 0);
            FV8Utils::IsolateData<IObjectMapper>(Isolate)->RemoveFromDelegate(Isolate, Context, DelegatePtr, v8::Local<v8::Function>::Cast(Info[0]));
        }
        else
        {
            FV8Utils::ThrowException(Isolate, "invalid arguments");
        }
    }

    void FMulticastDelegateWrapper::Clear(const v8::FunctionCallbackInfo<v8::Value>& Info)
    {
        v8::Isolate* Isolate = Info.GetIsolate();
        v8::Isolate::Scope IsolateScope(Isolate);
        v8::HandleScope HandleScope(Isolate);
        v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
        v8::Context::Scope ContextScope(Context);

        auto DelegatePtr = FV8Utils::GetPoninterFast<void>(Info.Holder(), 0);
        FV8Utils::IsolateData<IObjectMapper>(Isolate)->ClearDelegate(Isolate, Context, DelegatePtr);
    }

    void FMulticastDelegateWrapper::Broadcast(const v8::FunctionCallbackInfo<v8::Value>& Info)
    {
        v8::Isolate* Isolate = Info.GetIsolate();
        v8::Isolate::Scope IsolateScope(Isolate);
        v8::HandleScope HandleScope(Isolate);
        v8::Local<v8::Context> Context = Isolate->GetCurrentContext();
        v8::Context::Scope ContextScope(Context);

        auto DelegatePtr = FV8Utils::GetPoninterFast<void>(Info.Holder(), 0);
        FV8Utils::IsolateData<IObjectMapper>(Isolate)->ExecuteDelegate(Isolate, Context, Info, DelegatePtr);
    }
}